import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { Component, Input, ViewChild, ChangeDetectionStrategy } from '@angular/core';
import { AdvancedFilterComponent } from './advanced-filter.component';
import { FieldItem } from './interface';
import { AdvancedFilterModule } from './advanced-filter.module';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

const treeData = [
  {
    label: 'Documents',
    children: [
      {
        label: 'Expenses.doc',
        isLeaf: true,
      },
      {
        label: 'Resume.doc',
        isLeaf: true,
      },
    ],
  },
  {
    label: 'Word',
    children: [
      {
        label: 'Expenses.doc',
        isLeaf: true,
      },
      {
        label: 'Resume.doc',
        isLeaf: true,
      },
    ],
  },
  {
    label: 'Pictures',
    children: [
      {
        label: 'logo.png',
        isLeaf: true,
      },
      {
        label: 'banner.pnd',
        isLeaf: true,
      },
    ],
  },
];

const lvFields: FieldItem[] = [
  {
    label: 'Status',
    field: 'status',
    type: 'single',
    option: {
      items: [
        {
          label: 'Normal',
          value: 'normal',
        },
        {
          label: 'Error',
          value: 'error',
        },
      ],
    },
  },
  {
    label: 'Checker',
    field: 'checker',
    type: 'multiple',
    option: {
      items: [
        {
          label: 'Jack',
          value: 'Jack',
        },
        {
          label: 'Tom',
          value: 'Tom',
        },
        {
          label: 'Jerry',
          value: 'Jerry',
        },
      ],
      lvShowCheckAll: true,
      lvShowFilter: true,
    },
  },
  {
    label: 'CommitNumber',
    field: 'commitNumber',
    type: 'numberRange',
  },
  {
    label: 'IssueNumber',
    field: 'issueNumber',
    type: 'number',
  },
  {
    label: 'ReleaseVersion',
    field: 'releaseVersion',
    type: 'textInput',
    mode: 'startsWith',
  },
  {
    label: 'DateSelection',
    field: 'dateSelection',
    type: 'datePicker',
    option: {
      lvPickerMode: 'year',
      lvShowTodayButton: true,
    },
  },
  {
    label: 'DateRange',
    field: 'dateRange',
    type: 'dateRangePicker',
    mode: 'rangeIn',
  },
  {
    label: 'TreeSingle',
    field: 'treeSingle',
    type: 'treeSingle',
    option: {
      lvData: treeData,
    },
  },
  {
    label: 'TreeMultiple',
    field: 'treeMultiple',
    type: 'treeMultiple',
    option: {
      lvData: treeData,
      lvShowCheckbox: true,
    },
  },
];

const lvFieldsDefault: FieldItem[] = [
  {
    label: 'Status',
    field: 'status',
    type: 'single',
    defaultValue: 'normal',
    option: {
      items: [
        {
          label: 'Normal',
          value: 'normal',
        },
        {
          label: 'Error',
          value: 'error',
        },
      ],
    },
  },
  {
    label: 'Checker',
    field: 'checker',
    type: 'multiple',
    defaultValue: ['Tom', 'Jerry'],
    option: {
      items: [
        {
          label: 'Jack',
          value: 'Jack',
        },
        {
          label: 'Tom',
          value: 'Tom',
        },
        {
          label: 'Jerry',
          value: 'Jerry',
        },
      ],
    },
  },
];

@Component({
  template: `
    <lv-advanced-filter #advancedFilterComponent [lvFields]="fields" (lvOnChange)="onChange($event)"></lv-advanced-filter>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestAdvancedFilterComponent {
  @ViewChild('advancedFilterComponent', { static: true }) advancedFilterComponent: AdvancedFilterComponent;
  @Input() fields: FieldItem[];
  onChange() {}
}

describe('AdvancedFilterComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestAdvancedFilterComponent],
      imports: [AdvancedFilterModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: false },
    }).compileComponents();
  }));

  describe('render', () => {
    let component: TestAdvancedFilterComponent;
    let fixture: ComponentFixture<TestAdvancedFilterComponent>;

    beforeEach(() => {
      fixture = TestBed.createComponent(TestAdvancedFilterComponent);
      component = fixture.componentInstance;
      component.fields = lvFields;
      fixture.autoDetectChanges();
    });

    it('with lvFields', () => {
      expect(fixture).toMatchSnapshot();
    });
  });

  describe('filter type test', () => {
    let component: TestAdvancedFilterComponent;
    let fixture: ComponentFixture<TestAdvancedFilterComponent>;
    let input: HTMLElement;
    let testClickFilter: (label: string, index: number) => void;
    const optionClass = 'lv-option';
    const tagItemClass = '.lv-tag-item';
    const tagRemoveClass = '.lv-tag-remove';
    const inputClass = '.lv-input';
    const spinnerInputClass = '.lv-spinner-input';
    const primaryButtonClass = '.lv-button-type-primary';
    const treeOptionClass = '.lv-treenode-content';

    beforeEach(() => {
      fixture = TestBed.createComponent(TestAdvancedFilterComponent);
      component = fixture.componentInstance;
      component.fields = lvFields;
      jest.spyOn(component, 'onChange');
      fixture.autoDetectChanges();
      input = fixture.nativeElement.querySelector(inputClass);
      window.HTMLElement.prototype.scrollIntoView = () => {};

      // 点击弹出过滤器下拉列表，选择过滤器后生成对应的label显示
      testClickFilter = (label: string, index: number) => {
        input.click();
        tick(500);
        const option = document.querySelectorAll(optionClass)[index];
        expect(option.textContent).toContain(label);
        document.dispatchEvent(new Event('mouseover'));
        option.dispatchEvent(new Event('click'));
        tick(500);
        const tag = fixture.nativeElement.querySelector(tagItemClass);
        expect(tag.textContent).toContain(`${label}: `);
      };
    });

    it('should work with single type', fakeAsync(() => {
      testClickFilter('Status', 0);

      const option = document.querySelector(optionClass);
      expect(option.textContent).toContain('Normal');
      option.dispatchEvent(new Event('click'));
      tick(500);

      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain('Status: Normal'); // 单选过滤器选择Normal后，刷新对应过滤器tag标签
      expect(component.onChange).toBeCalledTimes(1); // 选择后触发一次onchange回调

      const removeIcon = fixture.nativeElement.querySelector(tagRemoveClass);
      removeIcon.dispatchEvent(new Event('click'));
      tick(500);
      expect(fixture.nativeElement.querySelector(tagItemClass)).toBeNull(); // tag标签能正常删除
      expect(component.onChange).toBeCalledTimes(2); // 删除后也会触发一次onchange回调
    }));

    it('should work with multiple type', fakeAsync(() => {
      testClickFilter('Checker', 1);

      const option1 = document.querySelectorAll(optionClass)[0];
      const option2 = document.querySelectorAll(optionClass)[1];
      const confirmButton = document.querySelector(primaryButtonClass);
      option1.dispatchEvent(new Event('click'));
      option2.dispatchEvent(new Event('click'));
      confirmButton.dispatchEvent(new Event('click'));

      tick(500);
      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain('Checker: Jack,Tom'); // 多选过滤器选择后，刷新对应过滤器tag标签
      expect(component.onChange).toBeCalledTimes(1);
    }));

    it('should work with number type', fakeAsync(() => {
      testClickFilter('IssueNumber', 3);

      const spinnerInput: HTMLInputElement = document.querySelector(spinnerInputClass);
      spinnerInput.value = '3';
      spinnerInput.dispatchEvent(new Event('input'));
      tick(500);
      const confirmButton = document.querySelectorAll(primaryButtonClass)[0];
      confirmButton.dispatchEvent(new Event('click'));

      tick(500);
      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain('IssueNumber: 3'); // 数字过滤器选择后，刷新对应过滤器tag标签
      expect(component.onChange).toBeCalledTimes(1);
    }));

    it('should work with numberRange type', fakeAsync(() => {
      testClickFilter('CommitNumber', 2);

      const spinnerInput1: HTMLInputElement = document.querySelectorAll(spinnerInputClass)[0] as HTMLInputElement;
      const spinnerInput2: HTMLInputElement = document.querySelectorAll(spinnerInputClass)[1] as HTMLInputElement;
      spinnerInput1.value = '3';
      spinnerInput1.dispatchEvent(new Event('input'));
      spinnerInput2.value = '5';
      spinnerInput2.dispatchEvent(new Event('input'));
      tick(500);
      const confirmButton = document.querySelectorAll(primaryButtonClass)[0];
      confirmButton.dispatchEvent(new Event('click'));

      tick(500);
      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain('CommitNumber: 3~5'); // 数字过滤器选择后，刷新对应过滤器tag标签
      expect(component.onChange).toBeCalledTimes(1);
    }));

    it('should work with textInput type', fakeAsync(() => {
      testClickFilter('ReleaseVersion', 4);

      const textInput: HTMLInputElement = document.querySelectorAll(inputClass)[1] as HTMLInputElement;
      textInput.value = 'test';
      textInput.dispatchEvent(new Event('input'));
      tick(500);
      const confirmButton = document.querySelectorAll(primaryButtonClass)[0];
      confirmButton.dispatchEvent(new Event('click'));

      tick(500);
      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain('ReleaseVersion: test');
      expect(component.onChange).toBeCalledTimes(1);
    }));

    it('should work with datePicker type', fakeAsync(() => {
      testClickFilter('DateSelection', 5);

      const year = document.querySelector('.lv-date-picker-year-cell');
      const yearDate = year.textContent;
      year.dispatchEvent(new Event('click'));
      tick(500);

      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain(`DateSelection: ${yearDate}`);
      expect(component.onChange).toBeCalledTimes(1);
    }));

    it('should work with treeSingle type', fakeAsync(() => {
      testClickFilter('TreeSingle', 7);

      const option = document.querySelector(treeOptionClass);
      option.dispatchEvent(new Event('click'));
      tick(500);

      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain(`TreeSingle: Documents`);
      expect(component.onChange).toBeCalledTimes(1);
    }));

    it('should work with treeMultiple type', fakeAsync(() => {
      testClickFilter('TreeMultiple', 8);

      const option1 = document.querySelectorAll(treeOptionClass)[0];
      const option2 = document.querySelectorAll(treeOptionClass)[1];
      const confirmButton = document.querySelector(primaryButtonClass);
      option1.dispatchEvent(new Event('click'));
      option2.dispatchEvent(new Event('click'));
      confirmButton.dispatchEvent(new Event('click'));
      tick(500);

      const tag = fixture.nativeElement.querySelector(tagItemClass);
      expect(tag.textContent).toContain('Documents');
      expect(tag.textContent).toContain('Word');
      expect(component.onChange).toBeCalledTimes(1);
    }));
  });

  describe('defaultValue and clear test', () => {
    let component: TestAdvancedFilterComponent;
    let fixture: ComponentFixture<TestAdvancedFilterComponent>;
    const tagItemClass = '.lv-tag-item';
    const clearButtonClass = '.lv-advanced-filter-clear-btn';

    beforeEach(() => {
      fixture = TestBed.createComponent(TestAdvancedFilterComponent);
      component = fixture.componentInstance;
      component.fields = lvFieldsDefault;
      fixture.autoDetectChanges();
    });

    it('should work with defaultValue', () => {
      const tag1 = fixture.nativeElement.querySelectorAll(tagItemClass)[0];
      const tag2 = fixture.nativeElement.querySelectorAll(tagItemClass)[1];
      expect(tag1.textContent).toContain('Status: Normal');
      expect(tag2.textContent).toContain('Checker: Tom,Jerry');
    });

    it('should clear all when click the clear button', fakeAsync(() => {
      const clearButton = fixture.nativeElement.querySelector(clearButtonClass);
      clearButton.dispatchEvent(new Event('click'));
      tick(500);
      expect(fixture.nativeElement.querySelector(tagItemClass)).toBeNull();
    }));
  });
});
